package cn.rtplay.esp32camcar;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.SystemClock;

import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import java.util.Arrays;

import cn.rtplay.esp32camcar.UDPUtils.UDPBuild;
import cn.rtplay.esp32camcar.toolUtils.LogUtil;

/**
 * The type Bluetooth le service.
 */
public class ESP32UDPService extends Service {
    public static final String BROADCAST_BATTERY_INFORMATION = "batteryInformation";
    public static final String BROADCAST_SPEED = "currentSpeed";
    public static final String BROADCAST_ELECTRICITY = "electricity";
    public static final String BROADCAST_CHARGING = "charging";

    public static final int CHARGING_STATUS_NOT_CHARGING = 0;
    public static final int CHARGING_STATUS_CHARGING = 1;
    public static final int CHARGING_STATUS_CHARGING_FULL = 2;

    private final IBinder mBinder = new LocalBinder();

    private final static String TAG = ESP32UDPService.class.getSimpleName();
    private UDPBuild udpBuild;
    private boolean mIsCorrection = false;// 是否在校正转向
    private int mCorrectionAngle;
    private boolean mIsStart = false;// 是否启动
    private int mChargingStatus = CHARGING_STATUS_NOT_CHARGING;// 充电状态
    private LocalBroadcastManager localBroadcastManager;
    /****
     数据协议格式
     data   定义         范围及用法
     0    协议头       固定为0x52(R)
     1    方向角度     有符号1字节，-128~127 负角度为左，正为右
     2    方向+速度    无符号1字节，最高位为方向0为前进，1为后退；低7位为速度百分比，有效值为0~100
     3    车灯状态位1  按位表示，H|左转向|右转向|雾灯|远光|近光|自动大灯|示廓灯|双闪|L
     4    车灯状态位2  按位表示，H|刹车灯|倒车|x|x|x|x|x|x|L，(刹车灯与倒车灯设置无效)
     ****/
    byte[] sendData = {0x52, 0x00, 0x00, 0x00, 0x00};
    byte[] lastSendData = {0x52, 0x00, 0x00, 0x00, 0x00};
    byte[] lastReceiveData = {0x70, 0x00, 0x00, 0x00, 0x00};
    /****
     控制协议格式：一次可同时设置两个设置项，如果多余的设置项为0，设置项与参数定义参见传输协议文档
     data   定义         范围及用法
     0    协议头       固定为0x54(T)
     1    设置项1
     2    设置参数1
     3    设置项2
     4    设置参数2
     ****/
    byte[] sendCMD = {0x54, 0x00, 0x00, 0x00, 0x00};
    byte[] lastSendCMD = {0x54, 0x00, 0x00, 0x00, 0x00};

    /**
     * Instantiates a new Bluetooth le service.
     */
    public ESP32UDPService() {
    }

    @Override
    public void onCreate() {
        LogUtil.v(TAG, "onCreate");
        super.onCreate();
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        udpBuild = UDPBuild.getUdpBuild();
        udpBuild.setUdpReceiveCallback(data -> {
            // UDP数据接收，数据格式
            //0	协议头	固定为0x70(p)
            //1	实时速度L	int16
            //2	实时速度H	int16
            //3	电量百分比	0~100%
            //4	充电状态	0：未充电，1：充电中，2：充电完成
            if (((data.getData())[0] == 'p') && (data.getLength() >= 5)) {
                byte[] receiveData = {0x70, 0x00, 0x00, 0x00, 0x00};
                System.arraycopy(data.getData(), 0, receiveData, 0, 5);
                if (!Arrays.equals(receiveData, lastReceiveData)) {
                    Intent intent = new Intent(BROADCAST_BATTERY_INFORMATION);
                    intent.putExtra(BROADCAST_SPEED, ((data.getData())[1] & 0xFF) | ((data.getData())[2] << 8 & 0xFF00));
                    intent.putExtra(BROADCAST_ELECTRICITY, (int) (data.getData())[3]);
                    intent.putExtra(BROADCAST_CHARGING, (int) (data.getData())[4]);
                    mChargingStatus = data.getData()[4];
                    localBroadcastManager.sendBroadcast(intent);
                }
                System.arraycopy(receiveData, 0, lastReceiveData, 0, 5);
            }
        });
        Thread thread = new Thread(() -> {
            int cnt = 0;
            while (true) {
                if (!Arrays.equals(sendData, lastSendData)) {
                    cnt = 0;
                    System.arraycopy(sendData, 0, lastSendData, 0, sendData.length);
                    udpBuild.sendRAMDate(lastSendData, lastSendData.length);
                    LogUtil.d(TAG, "sendData: " + String.format("%02X %02X %02X %02X %02X", lastSendData[0], lastSendData[1], lastSendData[2], lastSendData[3], lastSendData[4]));
                } else if (!Arrays.equals(sendCMD, lastSendCMD)) {
                    System.arraycopy(sendCMD, 0, lastSendCMD, 0, sendCMD.length);
                    udpBuild.sendRAMDate(lastSendCMD, lastSendCMD.length);
                    LogUtil.d(TAG, "sendCMD: " + String.format("%02X %02X %02X %02X", lastSendCMD[1], lastSendCMD[2], lastSendCMD[3], lastSendCMD[4]));
                }
                if ((lastSendData[2] & 0x7F) != 0) {
                    cnt++;
                    if (cnt > 10) { // 速度不为0时，没500ms钟发送一次保活数据
                        cnt = 0;
                        udpBuild.sendRAMDate(lastSendData, lastSendData.length);
                        LogUtil.d(TAG, "sendData: " + String.format("%02X %02X %02X %02X %02X", lastSendData[0], lastSendData[1], lastSendData[2], lastSendData[3], lastSendData[4]));
                    }
                }
                SystemClock.sleep(50);
            }
        });
        thread.setPriority(10);
        thread.start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        LogUtil.v(TAG, "onBind");
        //throw new UnsupportedOperationException("Not yet implemented");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        LogUtil.v(TAG, "onUnbind");
        // After using a given device, you should make sure that BluetoothGatt.close() is called
        // such that resources are cleaned up properly.  In this particular example, close() is
        // invoked when the UI is disconnected from the Service.
        return super.onUnbind(intent);
    }

    public void startUDPSocket() {
        udpBuild.startUDPSocket();
    }

    public void stopUDPSocket() {
        udpBuild.stopUDPSocket();
    }

    public boolean getStart() {
        return mIsStart;
    }

    /**
     * 设置方向角度
     *
     * @param angle 角度，顺时针为正值，逆时针为负值，角度范围-40~40
     */
    public void setDirectionAngle(int angle) {
        if (!mIsCorrection) {
            angle += mCorrectionAngle;
            if (angle < -40) {
                sendData[1] = -40;
            } else if (angle > 40) {
                sendData[1] = 40;
            } else {
                sendData[1] = (byte) angle;
            }
        }
    }

    public static final int DRIVE_FORWARD = 0;
    public static final int DRIVE_BACKWARDS = 1;

    /**
     * 设置行驶方向和速度
     *
     * @param speed     行驶速度百分比 0~100%
     * @param direction 行驶方向
     */
    public void setDriverSpeed(int speed, int direction) {
        if (speed > 100) {
            speed = 100;
        }
        if (direction == DRIVE_FORWARD) {
            sendData[2] = (byte) speed;
        } else if (direction == DRIVE_BACKWARDS) {
            sendData[2] = (byte) (speed | 0x80);
        }
    }

    /**
     * 设置进入角度校正模式
     *
     * @param enable ture进入校正模式，false退出校正模式
     */
    public void correctionAngleMode(boolean enable) {
        LogUtil.v(TAG, "correctionAngleMode:" + enable);
        mIsCorrection = enable;
        sendData[1] = (byte) mCorrectionAngle;
    }

    /**
     * 初始化校正角度
     *
     * @param angle 初始化角度
     */
    public void initCorrectionAngle(int angle) {
        mCorrectionAngle = angle;
    }

    /**
     * 设置校正角度
     *
     * @param angle 校正角度
     */
    public void setCorrectionAngle(int angle) {
        mCorrectionAngle = angle;
        sendData[1] = (byte) mCorrectionAngle;
    }

    /**
     * 设置启动
     *
     * @param start 0：停止，1：启动
     */
    public void setStart(boolean start) {
        mIsStart = start;
        sendCMD[1] = 1;
        sendCMD[2] = (byte) (start ? 1 : 0);
        if (!start) {
            sendCMD[3] = 5;
            sendCMD[4] = 1;
        } else {
            sendCMD[3] = 0;
            sendCMD[4] = 0;
        }
    }

    /**
     * 设置图像质量
     *
     * @param quality 图像质量 0：流程模式 1：标准模式 2：高清模式
     */
    public void setImageQuality(int quality) {
        sendCMD[1] = 2;
        sendCMD[2] = (byte) quality;
        sendCMD[3] = 0;
        sendCMD[4] = 0;
    }

    /**
     * 设置音量开关
     *
     * @param enable 0:关闭声音，1：打开声音
     */
    public void setSoundEnable(boolean enable) {
        sendCMD[1] = 3;
        if (enable) sendCMD[2] = 1;
        else sendCMD[2] = 0;
        sendCMD[3] = 0;
        sendCMD[4] = 0;
    }

    /**
     * 设置音量
     *
     * @param volume 音量值0~30
     */
    public void setVolume(int volume) {
        sendCMD[1] = 4;
        sendCMD[2] = (byte) volume;
        sendCMD[3] = 0;
        sendCMD[4] = 0;
    }

    /**
     * 触发喇叭声音
     */
    private byte horn_i = 0; // 静态变量，用于实现重复触发

    public void hornSound() {
        sendCMD[1] = 6;
        sendCMD[2] = horn_i++;
        sendCMD[3] = 0;
        sendCMD[4] = 0;
    }

    /**
     * 初始化图像和声音参数
     *
     * @param quality 图像质量 0：流程模式 1：标准模式 2：高清模式
     * @param enable  0:关闭声音，1：打开声音
     * @param volume  音量值0~30
     */
    public void initAVSetting(int quality, boolean enable, int volume) {
        new Thread(() -> {
            sendCMD[1] = 2;
            sendCMD[2] = (byte) quality;
            sendCMD[3] = 3;
            if (enable) sendCMD[4] = 1;
            else sendCMD[4] = 0;
            SystemClock.sleep(500);
            sendCMD[1] = 4;
            sendCMD[2] = (byte) volume;
            sendCMD[3] = 5;
            sendCMD[4] = 1;
        }).start();
    }

    /**
     * 设置车灯
     *
     * @param light  灯光类型
     * @param on_off true：开，false：关
     */
    public void setCarLight(int light, boolean on_off) {
        if (light == CarLedStatus.LIGHT_LEFT) {
            if (on_off) {
                sendData[3] &= 0b00111110;
                sendData[3] |= (byte) 0b10000000;
            } else {
                sendData[3] &= 0b01111111;
            }
        } else if (light == CarLedStatus.LIGHT_RIGHT) {
            if (on_off) {
                sendData[3] &= 0b00111110;
                sendData[3] |= 0b01000000;
            } else {
                sendData[3] &= (byte) 0b10111111;
            }
        } else if (light == CarLedStatus.LIGHT_WARNING) {
            if (on_off) {
                sendData[3] &= 0b00111110;
                sendData[3] |= 0b00000001;
            } else {
                sendData[3] &= (byte) 0b11111110;
            }
        } else if (light == CarLedStatus.LIGHT_WIDTH) {
            if (on_off) {
                sendData[3] |= 0b00000010;
            } else {
                sendData[3] &= (byte) 0b11111101;
            }
        } else if (light == CarLedStatus.LIGHT_FAR) {
            if (on_off) {
                sendData[3] &= (byte) 0b11100011;
                sendData[3] |= 0b00010000;
            } else {
                sendData[3] &= (byte) 0b11101111;
            }
        } else if (light == CarLedStatus.LIGHT_NEAR) {
            if (on_off) {
                sendData[3] &= (byte) 0b11100011;
                sendData[3] |= 0b00001000;
            } else {
                sendData[3] &= (byte) 0b11110111;
            }
        } else if (light == CarLedStatus.LIGHT_AUTO) {
            if (on_off) {
                sendData[3] &= (byte) 0b11100011;
                sendData[3] |= 0b00000100;
            } else {
                sendData[3] &= (byte) 0b11111011;
            }
        } else if (light == CarLedStatus.LIGHT_STOP) {
            if (on_off) {
                sendData[4] |= (byte) 0b10000000;
            } else {
                sendData[4] &= 0b01111111;
            }
        } else if (light == CarLedStatus.LIGHT_REVERSE) {
            if (on_off) {
                sendData[4] |= 0b01000000;
            } else {
                sendData[4] &= (byte) 0b10111111;
            }
        }
    }

    /**
     * 设置充电中状态
     *
     * @param charging true：充电中，false：未充电
     */
    public void setChargingState(int charging) {
        mChargingStatus = charging;
    }

    /**
     * 获取充电中状态
     *
     * @return 充电状态
     */
    public int getChargingState() {
        return mChargingStatus;
    }

    /**
     * The type Local binder.
     */
    public class LocalBinder extends Binder {
        /**
         * Gets service.
         *
         * @return the service
         */
        ESP32UDPService getService() {
            return ESP32UDPService.this;
        }
    }
}
